﻿using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
#if UNITY_EDITOR
using UnityEditor;
#endif
using UnityEngine;
using UnityEngine.UI;


public class AtlasDebugTool : MonoBehaviour
{
    
    [ContextMenuItem("与第1个元素同文件夹, 同packTag的Sprite填充", "InitByPackTag")]
    public Sprite[] m_Sprites; //要参与Debug的Sprite
    private int m_ShowSpriteIndex = 0;

    public RawImage m_RawImage; //显示Texture
    public RectTransform m_InnerUV; //展示Sprite的inner区域
    public RectTransform m_OuterUV; //展示Sprite的outer区域
    public RectTransform m_RectSize; //展示Sprite没裁掉空白的话的大小

    public Image m_ImgSprite;
    public RectTransform m_Padding; //padding区域
    public RectTransform m_Border; //border区域

    public Button m_BtnShowAtlas;
    public Button m_BtnNextSprite;
    public Button m_BtnPrevSprite;
    public Toggle m_TogSpriteNativeSize;
    public Toggle m_TogShowInner;
    public Toggle m_TogShowOuter;
    public Toggle m_TogShowRectSize;
    public Toggle m_TogShowBorder;
    public Toggle m_TogShowPadding;
    public Text m_TxtLog;

    void Awake()
    {
        Texture atlasTexture = null;
        for (var i = 0; i < m_Sprites.Length; ++i)
        {
            var sp = m_Sprites[i];
            if (null == sp) continue;

            if (null == atlasTexture)
            {
                atlasTexture = sp.texture;
            }
            else if (sp.texture != atlasTexture)
            {
                Debug.LogError($"Sprite的texture存在不一致");
                return;
            }
        }

        m_BtnShowAtlas.onClick.AddListener(OnClick_ShowAtlas);
        m_BtnNextSprite.onClick.AddListener(OnClick_NextSprite);
        m_BtnPrevSprite.onClick.AddListener(OnClick_PrevSprite);
        m_TogShowInner.onValueChanged.AddListener(OnValueChanged_ShowInner);
        m_TogShowOuter.onValueChanged.AddListener(OnValueChanged_ShowOuter);
        m_TogShowRectSize.onValueChanged.AddListener(OnValueChanged_ShowRectSize);
        m_TogShowBorder.onValueChanged.AddListener(OnValueChanged_ShowBorder);
        m_TogShowPadding.onValueChanged.AddListener(OnValueChanged_ShowPadding);
    }

    private void OnClick_ShowAtlas()
    {
        if (m_RawImage.texture != m_Sprites[0].texture)
        {
            m_RawImage.texture = m_Sprites[0].texture;
            m_RawImage.SetNativeSize();
            ShowSpriteInnerOuter();
        }
    }

    private void OnClick_NextSprite()
    {
        if (m_ShowSpriteIndex < m_Sprites.Length - 1)
        {
            m_ShowSpriteIndex++;
            ShowSpriteInnerOuter();
            Debug.Log($"next: {m_ShowSpriteIndex}");
        }
    }

    private void OnClick_PrevSprite()
    {
        if (m_ShowSpriteIndex > 0)
        {
            m_ShowSpriteIndex--;
            ShowSpriteInnerOuter();
            Debug.Log($"prev: {m_ShowSpriteIndex}");
        }
    }

    private void OnValueChanged_ShowInner(bool isOn)
    {
        m_InnerUV.gameObject.SetActive(isOn);
    }

    private void OnValueChanged_ShowOuter(bool isOn)
    {
        m_OuterUV.gameObject.SetActive(isOn);
    }

    private void OnValueChanged_ShowRectSize(bool isOn)
    {
        m_RectSize.gameObject.SetActive(isOn);
    }

    private void OnValueChanged_ShowBorder(bool isOn)
    {
        m_Border.gameObject.SetActive(isOn);
    }

    private void OnValueChanged_ShowPadding(bool isOn)
    {
        m_Padding.gameObject.SetActive(isOn);
    }

    void ShowSpriteInnerOuter()
    {
        var sp = m_Sprites[m_ShowSpriteIndex];
        if (null == sp)
            return;

        m_ImgSprite.sprite = sp;
        if (sp.border.sqrMagnitude > 0)
            m_ImgSprite.type = Image.Type.Sliced;
        else
            m_ImgSprite.type = Image.Type.Simple;
        if (m_TogSpriteNativeSize.isOn)
            m_ImgSprite.SetNativeSize();

        var sb = new StringBuilder();
        sb.Append($"===== sp: {sp.name}, pack: {sp.packed}, packMode:{sp.packingMode}, rot:{sp.packingRotation}, {AssetDatabase.GetAssetPath(sp)} \n");
        var spTex = sp.texture;
        sb.Append($"spTex: ({spTex.width}x{spTex.height}), name:{spTex.name}, texPath:{AssetDatabase.GetAssetPath(spTex)}\n");

        sb.Append($"texRect: {sp.textureRect.ToStr()}\n");
        sb.Append($"offset lb: {sp.textureRectOffset}\n");
        var offsetRight = sp.rect.width - (sp.textureRectOffset.x + sp.textureRect.width);
        var offsetTop = sp.rect.height - (sp.textureRectOffset.y + sp.textureRect.height);
        sb.Append($"offset rt: ({offsetRight}, {offsetTop})\n");
        sb.Append($"rect: {sp.rect.ToStr()}\n");
        sb.Append($"border: {sp.border.ToStr()}\n");

        var uv_i = UnityEngine.Sprites.DataUtility.GetInnerUV(sp);
        Rect rect_i = new Rect(uv_i.x * spTex.width, uv_i.y * spTex.height, (uv_i.z - uv_i.x) * spTex.width, (uv_i.w - uv_i.y) * spTex.height);
        sb.Append($"uv_i: {uv_i.ToStr()}\n");
        sb.Append($"rect_i: {rect_i.ToStr()}\n");
        m_InnerUV.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, rect_i.x, rect_i.width);
        m_InnerUV.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom, rect_i.y, rect_i.height);

        var uv_o = UnityEngine.Sprites.DataUtility.GetOuterUV(sp);
        Rect rect_o = new Rect(uv_o.x * spTex.width, uv_o.y * spTex.height, (uv_o.z - uv_o.x) * spTex.width, (uv_o.w - uv_o.y) * spTex.height);
        sb.Append($"uv_o: {uv_o.ToStr()}\n");
        sb.Append($"rect_o: {rect_o.ToStr()}\n");
        m_OuterUV.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, rect_o.x, rect_o.width);
        m_OuterUV.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom, rect_o.y, rect_o.height);

        Rect rect_full = new Rect(uv_o.x * spTex.width - sp.textureRectOffset.x, 
            uv_o.y * spTex.height - sp.textureRectOffset.y, sp.rect.width, sp.rect.height);
        m_RectSize.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Left, rect_full.x, rect_full.width);
        m_RectSize.SetInsetAndSizeFromParentEdge(RectTransform.Edge.Bottom, rect_full.y, rect_full.height);

        StartCoroutine(ShowPaddingAndBorderCo(sp));

        //if (m_TxtLog)
        //    m_TxtLog.text = sb.ToString();
        Debug.Log(sb.ToString());

        CopySpriteInfo(sp);
    }

    void CopySpriteInfo(Sprite sp)
    {
        var spTex = sp.texture;

        var sb = new StringBuilder();

        sb.Append($"textureRect: {sp.textureRect.ToStr()}\n");
        sb.Append($"textureRectOffset: {sp.textureRectOffset}\n");
        sb.Append($"rect: {sp.rect.ToStr()}\n");
        sb.Append($"border: {sp.border.ToStr()}\n");
        sb.Append($"bounds: {sp.bounds.ToStr()}\n");
        sb.Append($"pivot: {sp.pivot.ToStr()}\n");

        Vector4 padding = UnityEngine.Sprites.DataUtility.GetPadding(sp);
        sb.Append($"padding: {padding.ToStr()}\n");

        Vector4 innerUV = UnityEngine.Sprites.DataUtility.GetInnerUV(sp);
        Rect rect_i = new Rect(innerUV.x * spTex.width, innerUV.y * spTex.height, (innerUV.z - innerUV.x) * spTex.width, (innerUV.w - innerUV.y) * spTex.height);
        sb.Append($"innerUV: {innerUV.ToStr()}\n");
        sb.Append($"rect_i: {rect_i.ToStr()}\n");

        Vector4 outerUV = UnityEngine.Sprites.DataUtility.GetOuterUV(sp);
        Rect rect_o = new Rect(outerUV.x * spTex.width, outerUV.y * spTex.height, (outerUV.z - outerUV.x) * spTex.width, (outerUV.w - outerUV.y) * spTex.height);
        sb.Append($"outerUV: {outerUV.ToStr()}\n");
        sb.Append($"rect_o: {rect_o.ToStr()}\n");

        GUIUtility.systemCopyBuffer = sb.ToString();
    }

    IEnumerator ShowPaddingAndBorderCo(Sprite sp)
    {
        yield return new WaitForEndOfFrame();

        if (sp.border.sqrMagnitude > 0)
        {
            m_Padding.anchoredPosition = m_ImgSprite.m_VertScratch[0];
            m_Padding.sizeDelta = m_ImgSprite.m_VertScratch[3] - m_ImgSprite.m_VertScratch[0];

            m_Border.anchoredPosition = m_ImgSprite.m_VertScratch[1];
            m_Border.sizeDelta = m_ImgSprite.m_VertScratch[2] - m_ImgSprite.m_VertScratch[1];
        }
        else
        {
            m_Padding.sizeDelta = Vector2.zero;
            m_Border.sizeDelta = Vector2.zero;
        }

        for (var i = 0; i < m_ImgSprite.m_VertScratch.Length; ++i)
        {
            Debug.Log($"{m_ImgSprite.m_VertScratch[i].ToStr()}");
        }
    }

#if UNITY_EDITOR

    void InitByPackTag()
    {
        var sp = m_Sprites[0];
        if (null == sp)
        {
            Debug.Log($"第1个元素为空");
            return;
        }
        var assetPath = AssetDatabase.GetAssetPath(sp);

        var imp = AssetImporter.GetAtPath(assetPath) as TextureImporter;
        if (string.IsNullOrEmpty(imp.spritePackingTag)) return;
        Debug.Log($"第1个元素: packingTag: {imp.spritePackingTag}");

        var spriteDirPath = Path.GetDirectoryName(assetPath);
        //sb.Append($"dir: {dirPath}");

        //文件夹下的所有Sprite添加到atlas下
        var spriteList = new List<Sprite>();
        FindAndAddSprite(spriteList, spriteDirPath, "*.png", imp.spritePackingTag);
        FindAndAddSprite(spriteList, spriteDirPath, "*.jpg", imp.spritePackingTag);
        FindAndAddSprite(spriteList, spriteDirPath, "*.jpeg", imp.spritePackingTag);

        if (spriteList.Count > 0)
        {
            m_Sprites = spriteList.ToArray();
            EditorUtility.SetDirty(this);
        }
    }

    private void FindAndAddSprite(List<Sprite> resultSpriteList, string dirPath, string pattern, string packingTag)
    {
        var files = Directory.GetFiles(dirPath, pattern, SearchOption.TopDirectoryOnly);
        if (null == files || files.Length <= 0) return;

        //sb.Append($"{dirPath}, {pattern}, {files.Length}");
        for (var i = 0; i < files.Length; ++i)
        {
            var filePath = files[i];
            var imp = AssetImporter.GetAtPath(filePath) as TextureImporter;
            if (null == imp || imp.spritePackingTag != packingTag) continue;

            //sb.Append($"sprite file: {files[i]}");
            var arr = AssetDatabase.LoadAllAssetsAtPath(filePath);
            if (null == arr) continue;

            for (var j = 0; j < arr.Length; ++j)
            {
                var sprite = arr[j] as Sprite;
                if (null != sprite)
                {
                    resultSpriteList.Add(sprite);
                }
            }
        }
    }

#endif

}

internal static class ToStrTool
{
    public static string ToStr(this Vector4 v)
    {
        return $"({v.x}, {v.y}, {v.z}, {v.w})";
    }

    public static string ToStr(this Vector3 v)
    {
        return $"({v.x}, {v.y}, {v.z})";
    }

    public static string ToStr(this Vector2 v)
    {
        return $"({v.x}, {v.y})";
    }

    public static string ToStr(this Bounds v)
    {
        return $"center:{v.center.ToStr()}, extends:{v.extents.ToStr()}";
    }

    public static string ToStr(this Rect v)
    {
        return $"(x:{v.xMin}, y:{v.yMin}, w:{v.width}, h:{v.height})";
    }

}